import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

public class TypingGame extends JFrame {
    // 配置常量
    private static final int WIDTH = 800;
    private static final int HEIGHT = 600;
    private static final float BASE_SPEED = 1.8f;
    private static final int LETTER_SPAWN_RATE = 1300;
    private static final int MAX_ATTEMPTS = 10;
    private static final int MIN_DISTANCE = 60;

    // 游戏状态
    private final CopyOnWriteArrayList<FallingLetter> letters = new CopyOnWriteArrayList<>();
    private final List<GameEffect> effects = new CopyOnWriteArrayList<>();
    private final AtomicInteger score = new AtomicInteger(0);
    private volatile boolean gameRunning = true;
    private volatile boolean isPaused = false;
    private final BlockingQueue<Character> inputQueue = new LinkedBlockingQueue<>();

    // 界面组件
    private final GamePanel gamePanel;
    private long startTime;

    public TypingGame() {
        gamePanel = new GamePanel();
        initializeUI();
        setupKeyListeners();
        startGameThreads();
    }

    private void initializeUI() {
        setTitle("流畅打字游戏最终版");
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setResizable(false);
        
        Container contentPane = getContentPane();
        contentPane.setLayout(new BorderLayout());
        contentPane.add(gamePanel, BorderLayout.CENTER);
        
        setSize(WIDTH, HEIGHT);
        setLocationRelativeTo(null);
    }

    private void setupKeyListeners() {
        addKeyListener(new KeyAdapter() {
            @Override
            public void keyTyped(KeyEvent e) {
                if (!isPaused) {
                    inputQueue.offer(Character.toLowerCase(e.getKeyChar()));
                }
            }

            @Override
            public void keyPressed(KeyEvent e) {
                if (e.getKeyCode() == KeyEvent.VK_SPACE) {
                    isPaused = !isPaused;
                    SwingUtilities.invokeLater(gamePanel::repaint);
                }
            }
        });
    }

    private void startGameThreads() {
        ExecutorService executor = Executors.newFixedThreadPool(4);
        executor.execute(this::gameLoop);
        executor.execute(this::spawnLetters);
        executor.execute(this::updateLetters);
        executor.execute(this::processInput);
        executor.shutdown();
    }

    private void gameLoop() {
        startTime = System.currentTimeMillis();
        while (gameRunning) {
            if (!isPaused) {
                checkGameOver();
            }
            SwingUtilities.invokeLater(gamePanel::repaint);
            sleep(16);
        }
        showGameOver();
    }

    private void spawnLetters() {
        Random rand = new Random();
        while (gameRunning) {
            if (!isPaused) {
                char c = (char) (rand.nextInt(26) + 'a');
                int x = findValidPosition(rand);
                if (x != -1) {
                    letters.add(new FallingLetter(c, x, 0));
                }
            }
            sleep(LETTER_SPAWN_RATE);
        }
    }

    private int findValidPosition(Random rand) {
        for (int i = 0; i < MAX_ATTEMPTS; i++) {
            int x = rand.nextInt(WIDTH - 60);
            if (letters.stream().noneMatch(l -> Math.abs(l.x - x) < MIN_DISTANCE)) {
                return x;
            }
        }
        return -1;
    }

    private void updateLetters() {
        while (gameRunning) {
            if (!isPaused) {
                letters.removeIf(l -> l.y > HEIGHT);
                letters.forEach(l -> l.fall(BASE_SPEED));
            }
            sleep(30);
        }
    }

    private void processInput() {
        while (gameRunning) {
            try {
                Character ch = inputQueue.poll(50, TimeUnit.MILLISECONDS);
                if (ch != null) handleInput(ch);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }
    }

    private void handleInput(char ch) {
        letters.stream()
            .filter(l -> l.getChar() == ch)
            .findFirst()
            .ifPresent(letter -> {
                letters.remove(letter);
                score.addAndGet(10);
                SwingUtilities.invokeLater(() -> 
                    effects.add(new HitEffect(letter.x, (int)letter.y))
                );
            });
    }

    private void checkGameOver() {
        if (letters.stream().anyMatch(l -> l.y > HEIGHT)) {
            gameRunning = false;
        }
    }

    private void showGameOver() {
        long duration = (System.currentTimeMillis() - startTime) / 1000;
        String message = String.format("游戏结束!%n最终得分: %d%n游戏时间: %d秒", score.get(), duration);
        SwingUtilities.invokeLater(() -> 
            JOptionPane.showMessageDialog(this, message));
    }

    private void sleep(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }

    private class GamePanel extends JPanel {
        @Override
        public Dimension getPreferredSize() {
            return new Dimension(WIDTH, HEIGHT);
        }

        @Override
        protected void paintComponent(Graphics g) {
            super.paintComponent(g);
            Graphics2D g2d = (Graphics2D) g;
            
            // 绘制背景
            g2d.setColor(new Color(25, 25, 25));
            g2d.fillRect(0, 0, WIDTH, HEIGHT);
            
            // 绘制游戏元素
            drawGameElements(g2d);
            
            // 绘制暂停界面
            if (isPaused) drawPauseScreen(g2d);
        }

        private void drawGameElements(Graphics2D g2d) {
            // 绘制分数
            g2d.setColor(Color.WHITE);
            g2d.setFont(new Font("Consolas", Font.BOLD, 24));
            g2d.drawString("得分: " + score.get(), 20, 30);

            // 绘制字母
            letters.forEach(l -> l.draw(g2d));

            // 绘制特效
            effects.removeIf(effect -> {
                effect.update();
                effect.draw(g2d);
                return effect.isComplete();
            });
        }

        private void drawPauseScreen(Graphics2D g2d) {
            g2d.setComposite(AlphaComposite.SrcOver.derive(0.7f));
            g2d.setColor(Color.DARK_GRAY);
            g2d.fillRect(0, 0, WIDTH, HEIGHT);
            
            g2d.setComposite(AlphaComposite.SrcOver);
            g2d.setColor(Color.WHITE);
            g2d.setFont(new Font("微软雅黑", Font.BOLD, 48));
            
            String text = "游戏暂停 - 按空格继续";
            int textWidth = g2d.getFontMetrics().stringWidth(text);
            g2d.drawString(text, (WIDTH - textWidth)/2, HEIGHT/2);
        }
    }

    interface GameEffect {
        void update();
        void draw(Graphics2D g2d);
        boolean isComplete();
    }

    private static class FallingLetter implements GameEffect {
        final char character;
        final Color color;
        final int x;
        float y;

        FallingLetter(char c, int x, int y) {
            this.character = c;
            this.x = x;
            this.y = y;
            this.color = new Color(
                ThreadLocalRandom.current().nextInt(100, 256),
                ThreadLocalRandom.current().nextInt(100, 256),
                ThreadLocalRandom.current().nextInt(100, 256)
            );
        }

        void fall(float speed) {
            y += speed;
        }

        @Override
        public void draw(Graphics2D g2d) {
            g2d.setColor(color);
            g2d.setFont(new Font("Consolas", Font.BOLD, 36));
            g2d.drawString(String.valueOf(character), x, (int)y);
        }

        @Override public void update() {}
        @Override public boolean isComplete() { return false; }
        public char getChar() { return character; }
    }

    private static class HitEffect implements GameEffect {
        private final int x;
        private int y;
        private float alpha = 1.0f;
        private float scale = 1.0f;

        HitEffect(int x, int y) {
            this.x = x;
            this.y = y;
        }

        @Override
        public void update() {
            scale += 0.2f;
            alpha = Math.max(0, alpha - 0.03f); // 修复alpha越界问题
            y -= 2;
        }

        @Override
        public void draw(Graphics2D g2d) {
            if (alpha <= 0) return;
            
            Composite original = g2d.getComposite();
            g2d.setComposite(AlphaComposite.SrcOver.derive(alpha));
            g2d.setColor(Color.YELLOW);
            g2d.setFont(new Font("Arial", Font.BOLD, (int)(36 * scale)));
            g2d.drawString("★", x, y);
            g2d.setComposite(original);
        }

        @Override
        public boolean isComplete() {
            return alpha <= 0;
        }
    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            TypingGame game = new TypingGame();
            game.setVisible(true);
        });
    }
}